#include "asio_AsyncConnector.h"
#include "asio_NetworkHandler.h"
#include "asio_NetworkPacket.h"
#include "asio_header.h"
#include "ClientHandler.h"

namespace XGC
{
	namespace net
	{
		xgc_void handle_connect( deadline_timer* Timer, asio_SocketPtr pSocket, ProtocalPtr pProtocal, IMessageQueue* pMessageQueue, const system::error_code& error )
		{
			if (!error)
			{
				pSocket->accpet( (xgc_long_ptr)Timer );
			}
			else
			{
				pSocket->close();
				if( pMessageQueue )
				{
					asio_NetworkPacket* pPacket = new asio_NetworkPacket( -1, xgc_nullptr );
					if( pPacket )
					{
						int error_code_value = error.value();
						pProtocal->packet_system( EVENT_CONNECT, const_cast< xgc_byte* >( pPacket->base() ) );
						pPacket->seek( asio_NetworkPacket::rw, pProtocal->header_space( pPacket->userdata() ) );
						pPacket->putn( (const xgc_byte*)&error_code_value, sizeof(error_code_value) );
						pPacket->putn( (const xgc_byte*)error.message().c_str(), error.message().length() + 1 );
						pProtocal->packet_finial( const_cast< xgc_byte* >( pPacket->base() ), pPacket->size() );
						pMessageQueue->PushMessage( pPacket, true );
					}
					pMessageQueue->Release();
				}
			}

			Timer->cancel();
		}

		xgc_void handle_timeout( deadline_timer* Timer, asio_SocketPtr pSocket, ProtocalPtr pProtocal, IMessageQueue* pMessageQueue, const system::error_code& error )
		{
			if( pSocket->is_connected() )
				return;

			pSocket->close();

			if( pMessageQueue && ( !error || error.value() == 995 ) )
			{
				asio_NetworkPacket* pPacket = new asio_NetworkPacket( -1, xgc_nullptr );
				if( pPacket )
				{
					int error_code_value = error.value();
					pProtocal->packet_system( EVENT_CONNECT, const_cast< xgc_byte* >( pPacket->base() ) );
					pPacket->seek( asio_NetworkPacket::rw , pProtocal->header_space( pPacket->userdata() ) );
					pPacket->putn( (const xgc_byte*)&error_code_value, sizeof(error_code_value) );
					pPacket->putn( (const xgc_byte*)error.message().c_str(), error.message().length() + 1 );
					pProtocal->packet_finial( const_cast< xgc_byte* >( pPacket->base() ), pPacket->size() );
					pMessageQueue->PushMessage( pPacket, true );
				}
				pMessageQueue->Release();
			}

			SAFE_DELETE( Timer )
		}

		xgc_lpvoid AsyncConnect( io_service& service, const char* pszAddress, xgc_int16 port, PacketProtocal &protocal, IMessageQueue* pMessageQueue, xgc_uint16 nTimerInvent, xgc_uint16 nTimeout )
		{
			system::error_code ec;
			ip::address_v4 addr( ip::address_v4::from_string( pszAddress, ec ) );
			if( ec )
				return xgc_nullptr;

			asio_Socket *pSocketRing = xgc_nullptr;
			ProtocalPtr protocal_ptr( new PacketProtocal( protocal ) );
			asio_SocketPtr pSocket( pSocketRing = new asio_Socket( service, protocal_ptr, new CClientHandler( pMessageQueue, protocal_ptr.get() ), nTimerInvent, nTimeout ) );

			// set timeout callback
			deadline_timer *Timer = new deadline_timer( service );
			Timer->expires_from_now(boost::posix_time::millisec(nTimeout?nTimeout:30000));
			Timer->async_wait( boost::bind( handle_timeout, Timer, pSocket, protocal_ptr, pMessageQueue, asio::placeholders::error ) );

			// async connect server.
			pSocket->socket().async_connect( 
				ip::tcp::endpoint( addr, port ), 
				boost::bind( handle_connect, Timer, pSocket, protocal_ptr, pMessageQueue, asio::placeholders::error )
				);

			return (xgc_lpvoid)Timer;
		}

		xgc_bool DirectConnect( io_service& service, const char* pszAddress, xgc_int16 port, PacketProtocal &protocal, IMessageQueue* pMessageQueue, xgc_uint16 nTimerInvent, xgc_uint16 nTimeout )
		{
			system::error_code ec;
			ip::address_v4 addr( ip::address_v4::from_string( pszAddress, ec ) );
			if( ec )
				return false;

			ProtocalPtr protocal_ptr( new PacketProtocal( protocal ) );
			asio_SocketPtr pSocket( new asio_Socket( service, protocal_ptr, new CClientHandler( pMessageQueue, protocal_ptr.get() ), nTimerInvent, nTimeout ) );

			if( pSocket->socket().connect( ip::tcp::endpoint( addr, port ), ec ) == 0 )
			{
				handle_connect( xgc_nullptr, pSocket, protocal_ptr, pMessageQueue, ec );
			}

			return !ec;
		}
	}
}